#ifndef __HardwareBufferManager__
#define __HardwareBufferManager__


#include "U2PreRequest.h"
#include "U2MemoryAllocatorConfig.h"
#include "U2Singleton.h"
#include "U2HardwareVertexBuffer.h"
#include "U2HardwareIndexBuffer.h"
#include "U2VertexIndexData.h"


U2EG_NAMESPACE_BEGIN


/** Abstract interface representing a 'licensee' of a hardware buffer copy.
remarks
    Often it's useful to have temporary buffers which are used for working
    but are not necessarily needed permanently. However, creating and 
    destroying buffers is expensive, so we need a way to share these 
    working areas, especially those based on existing fixed buffers. 
    This class represents a licensee of one of those temporary buffers, 
    and must be implemented by any user of a temporary buffer if they 
    wish to be notified when the license is expired. 
*/
class _U2Share U2HardwareBufferLicensee
{
public:
    virtual ~U2HardwareBufferLicensee() { }
    /** This method is called when the buffer license is expired and is about
    to be returned to the shared pool. */
    virtual void licenseExpired(U2HardwareBuffer* buffer) = 0;
};



/** Structure for recording the use of temporary blend buffers */
class _U2Share U2TempBlendedBufferInfo : public U2HardwareBufferLicensee, public BufferAlloc
{
private:
    // Pre-blended 
    U2HardwareVertexBufferSharedPtr srcPositionBuffer;
    U2HardwareVertexBufferSharedPtr srcNormalBuffer;
    // Post-blended 
    U2HardwareVertexBufferSharedPtr destPositionBuffer;
    U2HardwareVertexBufferSharedPtr destNormalBuffer;
    /// Both positions and normals are contained in the same buffer
    bool posNormalShareBuffer;
    unsigned short posBindIndex;
    unsigned short normBindIndex;
    bool bindPositions;
    bool bindNormals;

public:
    ~U2TempBlendedBufferInfo(void);
    /// Utility method, extract info from the given VertexData
    void extractFrom(const U2VertexData* sourceData);
    /// Utility method, checks out temporary copies of src into dest
    void checkoutTempCopies(bool positions = true, bool normals = true);
    /// Utility method, binds dest copies into a given VertexData struct
    void bindTempCopies(U2VertexData* targetData, bool suppressHardwareUpload);
    /** Overridden member from U2HardwareBufferLicensee. */
    void licenseExpired(U2HardwareBuffer* buffer);
	/** Detect currently have buffer copies checked out and touch it */
	bool buffersCheckedOut(bool positions = true, bool normals = true) const;
};


/** Base definition of a hardware buffer manager.
@remarks
	This class is deliberately not a Singleton, so that multiple types can 
	exist at once. The Singleton is wrapped via the Decorator pattern
	in U2HardwareBufferManager, below. Each concrete implementation should
	provide a subclass of U2HardwareBufferManagerBase, which does the actual
	work, and also a very simple subclass of U2HardwareBufferManager which 
	simply constructs the instance of the U2HardwareBufferManagerBase subclass 
	and passes it to the U2HardwareBufferManager superclass as a delegate. 
	This subclass must also delete the implementation instance it creates.
*/
class _U2Share U2HardwareBufferManagerBase : public BufferAlloc
{
    friend class U2HardwareVertexBufferSharedPtr;
    friend class U2HardwareIndexBufferSharedPtr;
protected:
    /** WARNING: The following two members should place before all other members.
        Members destruct order is very important here, because destructing other
        members will cause notify back to this class, and then will access to this
        two members.
    */
    typedef std::set<U2HardwareVertexBuffer*> VertexBufferList;
    typedef std::set<U2HardwareIndexBuffer*> IndexBufferList;
    VertexBufferList mVertexBuffers;
    IndexBufferList mIndexBuffers;


    typedef std::set<U2VertexDeclaration*> VertexDeclarationList;
	typedef std::set<U2VertexBufferBinding*> VertexBufferBindingList;
    VertexDeclarationList mVertexDeclarations;
	VertexBufferBindingList mVertexBufferBindings;

	// Mutexes
	U2_MUTEX(mVertexBuffersMutex)
	U2_MUTEX(mIndexBuffersMutex)
	U2_MUTEX(mVertexDeclarationsMutex)
	U2_MUTEX(mVertexBufferBindingsMutex)

    /// Internal method for destroys all vertex declarations
    virtual void destroyAllDeclarations(void);
    /// Internal method for destroys all vertex buffer bindings
    virtual void destroyAllBindings(void);

    /// Internal method for creates a new vertex declaration, may be overridden by certain rendering APIs
    virtual U2VertexDeclaration* createVertexDeclarationImpl(void);
    /// Internal method for destroys a vertex declaration, may be overridden by certain rendering APIs
    virtual void destroyVertexDeclarationImpl(U2VertexDeclaration* decl);

	/// Internal method for creates a new U2VertexBufferBinding, may be overridden by certain rendering APIs
	virtual U2VertexBufferBinding* createVertexBufferBindingImpl(void);
	/// Internal method for destroys a U2VertexBufferBinding, may be overridden by certain rendering APIs
	virtual void destroyVertexBufferBindingImpl(U2VertexBufferBinding* binding);

public:

    enum BufferLicenseType
    {
        /// Licensee will only release buffer when it says so
        BLT_MANUAL_RELEASE,
        /// Licensee can have license revoked
        BLT_AUTOMATIC_RELEASE
    };

protected:
    /** Struct holding details of a license to use a temporary shared buffer. */
    class _U2Private U2VertexBufferLicense
    {
    public:
        U2HardwareVertexBuffer* originalBufferPtr;
        BufferLicenseType licenseType;
        size_t expiredDelay;
        U2HardwareVertexBufferSharedPtr buffer;
        U2HardwareBufferLicensee* licensee;
        U2VertexBufferLicense(
            U2HardwareVertexBuffer* orig,
            BufferLicenseType ltype, 
            size_t delay,
            U2HardwareVertexBufferSharedPtr buf, 
            U2HardwareBufferLicensee* lic) 
            : originalBufferPtr(orig)
            , licenseType(ltype)
            , expiredDelay(delay)
            , buffer(buf)
            , licensee(lic)
        {}

    };

    /// Map from original buffer to temporary buffers
    typedef std::multimap<U2HardwareVertexBuffer*, U2HardwareVertexBufferSharedPtr> FreeTemporaryVertexBufferMap;
    /// Map of current available temp buffers 
    FreeTemporaryVertexBufferMap mFreeTempVertexBufferMap;
    /// Map from temporary buffer to details of a license
    typedef std::map<U2HardwareVertexBuffer*, U2VertexBufferLicense> TemporaryVertexBufferLicenseMap;
    /// Map of currently licensed temporary buffers
    TemporaryVertexBufferLicenseMap mTempVertexBufferLicenses;
    /// Number of frames elapsed since temporary buffers utilization was above half the available
    size_t mUnderUsedFrameCount;
    /// Number of frames to wait before free unused temporary buffers
    static const size_t UNDER_USED_FRAME_THRESHOLD;
    /// Frame delay for BLT_AUTOMATIC_RELEASE temporary buffers
    static const size_t EXPIRED_DELAY_FRAME_THRESHOLD;
	// Mutexes
	U2_MUTEX(mTempBuffersMutex)


    /// Creates  a new buffer as a copy of the source, does not copy data
    virtual U2HardwareVertexBufferSharedPtr makeBufferCopy(
        const U2HardwareVertexBufferSharedPtr& source, 
        U2HardwareBuffer::Usage usage, bool useShadowBuffer);

public:
    U2HardwareBufferManagerBase();
    virtual ~U2HardwareBufferManagerBase();
	/** Create a hardware vertex buffer.
    @remarks
        This method creates a new vertex buffer; this will act as a source of geometry
        data for rendering objects. Note that because the meaning of the contents of
        the vertex buffer depends on the usage, this method does not specify a
        vertex format; the user of this buffer can actually insert whatever data 
        they wish, in any format. However, in order to use this with a RenderOperation,
        the data in this vertex buffer will have to be associated with a semantic element
        of the rendering pipeline, e.g. a position, or texture coordinates. This is done 
        using the U2VertexDeclaration class, which itself contains VertexElement structures
        referring to the source data.
    @remarks Note that because vertex buffers can be shared, they are reference
        counted so you do not need to worry about destroying themm this will be done
        automatically.
    @param vertexSize The size in bytes of each vertex in this buffer; you must calculate
        this based on the kind of data you expect to populate this buffer with.
    @param numVerts The number of vertices in this buffer.
    @param usage One or more members of the U2HardwareBuffer::Usage enumeration; you are
        strongly advised to use HBU_STATIC_WRITE_ONLY wherever possible, if you need to 
        update regularly, consider HBU_DYNAMIC_WRITE_ONLY and useShadowBuffer=true.
	@param useShadowBuffer If set to true, this buffer will be 'shadowed' by one stored in 
        system memory rather than GPU or AGP memory. You should set this flag if you intend 
        to read data back from the vertex buffer, because reading data from a buffer
		in the GPU or AGP memory is very expensive, and is in fact impossible if you
        specify HBU_WRITE_ONLY for the main buffer. If you use this option, all 
        reads and writes will be done to the shadow buffer, and the shadow buffer will
        be synchronised with the real buffer at an appropriate time.
    */
	virtual U2HardwareVertexBufferSharedPtr 
        createVertexBuffer(size_t vertexSize, size_t numVerts, U2HardwareBuffer::Usage usage, 
		bool useShadowBuffer = false) = 0;
	/** Create a hardware index buffer.
    @remarks Note that because buffers can be shared, they are reference
        counted so you do not need to worry about destroying them this will be done
        automatically.
	@param itype The type in index, either 16- or 32-bit, depending on how many vertices
		you need to be able to address
	@param numIndexes The number of indexes in the buffer
    @param usage One or more members of the U2HardwareBuffer::Usage enumeration.
	@param useShadowBuffer If set to true, this buffer will be 'shadowed' by one stored in 
        system memory rather than GPU or AGP memory. You should set this flag if you intend 
        to read data back from the index buffer, because reading data from a buffer
		in the GPU or AGP memory is very expensive, and is in fact impossible if you
        specify HBU_WRITE_ONLY for the main buffer. If you use this option, all 
        reads and writes will be done to the shadow buffer, and the shadow buffer will
        be synchronised with the real buffer at an appropriate time.
    */
	virtual U2HardwareIndexBufferSharedPtr 
        createIndexBuffer(U2HardwareIndexBuffer::IndexType itype, size_t numIndexes, 
		U2HardwareBuffer::Usage usage, bool useShadowBuffer = false) = 0;


    /** Creates a new vertex declaration. */
    virtual U2VertexDeclaration* createVertexDeclaration(void);
    /** Destroys a vertex declaration. */
    virtual void destroyVertexDeclaration(U2VertexDeclaration* decl);

	/** Creates a new U2VertexBufferBinding. */
	virtual U2VertexBufferBinding* createVertexBufferBinding(void);
	/** Destroys a U2VertexBufferBinding. */
	virtual void destroyVertexBufferBinding(U2VertexBufferBinding* binding);

	/** Registers a vertex buffer as a copy of another.
	@remarks
		This is useful for registering an existing buffer as a temporary buffer
		which can be allocated just like a copy.
	*/
	virtual void registerVertexBufferSourceAndCopy(
		const U2HardwareVertexBufferSharedPtr& sourceBuffer,
		const U2HardwareVertexBufferSharedPtr& copy);

    /** Allocates a copy of a given vertex buffer.
    @remarks
        This method allocates a temporary copy of an existing vertex buffer.
        This buffer is subsequently stored and can be made available for 
        other purposes later without incurring the cost of construction / 
        destruction.
    @param sourceBuffer The source buffer to use as a copy
    @param licenseType The type of license required on this buffer - automatic
        release causes this class to release licenses every frame so that 
        they can be reallocated anew.
    @param licensee Pointer back to the class requesting the copy, which must
        implement HardwareBufferLicense in order to be notified when the license
        expires.
    @param copyData If true, the current data is copied as well as the 
        structure of the buffer
    */
    virtual U2HardwareVertexBufferSharedPtr allocateVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& sourceBuffer, 
        BufferLicenseType licenseType,
        U2HardwareBufferLicensee* licensee,
        bool copyData = false);

    /** Manually release a vertex buffer copy for others to subsequently use.
    @remarks
        Only required if the original call to allocateVertexBufferCopy
        included a licenseType of BLT_MANUAL_RELEASE. 
    @param bufferCopy The buffer copy. The caller is expected to delete
        or at least no longer use this reference, since another user may
        well begin to modify the contents of the buffer.
    */
    virtual void releaseVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& bufferCopy); 

    /** Tell engine that the vertex buffer copy intent to reuse.
    @remarks
        Ogre internal keep an expired delay counter of BLT_AUTOMATIC_RELEASE
        buffers, when the counter count down to zero, it'll release for other
        purposes later. But you can use this function to reset the counter to
        the internal configured value, keep the buffer not get released for
        some frames.
    @param bufferCopy The buffer copy. The caller is expected to keep this
        buffer copy for use.
    */
    virtual void touchVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& bufferCopy);

    /** Free all unused vertex buffer copies.
    @remarks
        This method free all temporary vertex buffers that not in used.
        In normally, temporary vertex buffers are subsequently stored and can
        be made available for other purposes later without incurring the cost
        of construction / destruction. But in some cases you want to free them
        to save hardware memory (e.g. application was runs in a long time, you
        might free temporary buffers periodically to avoid memory overload).
    */
    virtual void _freeUnusedBufferCopies(void);

    /** Internal method for releasing all temporary buffers which have been 
       allocated using BLT_AUTOMATIC_RELEASE; is called by OGRE.
    @param forceFreeUnused If true, free all unused temporary buffers.
        If false, auto detect and free all unused temporary buffers based on
        temporary buffers utilization.
    */
    virtual void _releaseBufferCopies(bool forceFreeUnused = false);

    /** Internal method that forces the release of copies of a given buffer.
    @remarks
        This usually means that the buffer which the copies are based on has
        been changed in some fundamental way, and the owner of the original 
        wishes to make that known so that new copies will reflect the
        changes.
    @param sourceBuffer the source buffer as a shared pointer.  Any buffer copies created from the source buffer
        are deleted.
    */
    virtual void _forceReleaseBufferCopies(
        const U2HardwareVertexBufferSharedPtr& sourceBuffer);

    /** Internal method that forces the release of copies of a given buffer.
    @remarks
        This usually means that the buffer which the copies are based on has
        been changed in some fundamental way, and the owner of the original 
        wishes to make that known so that new copies will reflect the
        changes.
    @param sourceBuffer the source buffer as a pointer.  Any buffer copies created from the source buffer
        are deleted.
    */
    virtual void _forceReleaseBufferCopies(U2HardwareVertexBuffer* sourceBuffer);

	/// Notification that a hardware vertex buffer has been destroyed
	void _notifyVertexBufferDestroyed(U2HardwareVertexBuffer* buf);
	/// Notification that a hardware index buffer has been destroyed
	void _notifyIndexBufferDestroyed(U2HardwareIndexBuffer* buf);

};

/** Singleton wrapper for hardware buffer manager. */
class _U2Share U2HardwareBufferManager : public U2Singleton<U2HardwareBufferManager>, public U2HardwareBufferManagerBase
{
    friend class U2HardwareVertexBufferSharedPtr;
    friend class U2HardwareIndexBufferSharedPtr;

protected:
	U2HardwareBufferManagerBase* mImpl;

public:
    static U2HardwareBufferManager&		getSingleton(void);
    static U2HardwareBufferManager*		getSingletonPtr(void);

	U2HardwareBufferManager(U2HardwareBufferManagerBase* imp);
	~U2HardwareBufferManager();

	/** @copydoc HardwareBufferManagerInterface::createVertexBuffer */
	U2HardwareVertexBufferSharedPtr 
        createVertexBuffer(size_t vertexSize, size_t numVerts, U2HardwareBuffer::Usage usage, 
		bool useShadowBuffer = false)
	{
		return mImpl->createVertexBuffer(vertexSize, numVerts, usage, useShadowBuffer);
	}
	/** @copydoc HardwareBufferManagerInterface::createIndexBuffer */
	U2HardwareIndexBufferSharedPtr 
        createIndexBuffer(U2HardwareIndexBuffer::IndexType itype, size_t numIndexes, 
		U2HardwareBuffer::Usage usage, bool useShadowBuffer = false)
	{
		return mImpl->createIndexBuffer(itype, numIndexes, usage, useShadowBuffer);
	}

	/** @copydoc HardwareBufferManagerInterface::createVertexDeclaration */
	virtual U2VertexDeclaration* createVertexDeclaration(void)
	{
		return mImpl->createVertexDeclaration();
	}
	/** @copydoc HardwareBufferManagerInterface::destroyVertexDeclaration */
    virtual void destroyVertexDeclaration(U2VertexDeclaration* decl)
	{
		mImpl->destroyVertexDeclaration(decl);
	}

	/** @copydoc HardwareBufferManagerInterface::createVertexBufferBinding */
	virtual U2VertexBufferBinding* createVertexBufferBinding(void)
	{
		return mImpl->createVertexBufferBinding();
	}
	/** @copydoc HardwareBufferManagerInterface::destroyVertexBufferBinding */
	virtual void destroyVertexBufferBinding(U2VertexBufferBinding* binding)
	{
		mImpl->destroyVertexBufferBinding(binding);
	}
	/** @copydoc HardwareBufferManagerInterface::registerVertexBufferSourceAndCopy */
	virtual void registerVertexBufferSourceAndCopy(
		const U2HardwareVertexBufferSharedPtr& sourceBuffer,
		const U2HardwareVertexBufferSharedPtr& copy)
	{
		mImpl->registerVertexBufferSourceAndCopy(sourceBuffer, copy);
	}
	/** @copydoc HardwareBufferManagerInterface::allocateVertexBufferCopy */
    virtual U2HardwareVertexBufferSharedPtr allocateVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& sourceBuffer, 
        BufferLicenseType licenseType,
        U2HardwareBufferLicensee* licensee,
        bool copyData = false)
	{
		return mImpl->allocateVertexBufferCopy(sourceBuffer, licenseType, licensee, copyData);
	}
	/** @copydoc HardwareBufferManagerInterface::releaseVertexBufferCopy */
    virtual void releaseVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& bufferCopy)
	{
		mImpl->releaseVertexBufferCopy(bufferCopy);
	}

	/** @copydoc HardwareBufferManagerInterface::touchVertexBufferCopy */
    virtual void touchVertexBufferCopy(
        const U2HardwareVertexBufferSharedPtr& bufferCopy)
	{
		mImpl->touchVertexBufferCopy(bufferCopy);
	}

	/** @copydoc HardwareBufferManagerInterface::_freeUnusedBufferCopies */
    virtual void _freeUnusedBufferCopies(void)
	{
		mImpl->_freeUnusedBufferCopies();
	}
	/** @copydoc HardwareBufferManagerInterface::_releaseBufferCopies */
    virtual void _releaseBufferCopies(bool forceFreeUnused = false)
	{
		mImpl->_releaseBufferCopies(forceFreeUnused);
	}
	/** @copydoc HardwareBufferManagerInterface::_forceReleaseBufferCopies */
    virtual void _forceReleaseBufferCopies(
        const U2HardwareVertexBufferSharedPtr& sourceBuffer)
	{
		mImpl->_forceReleaseBufferCopies(sourceBuffer);
	}
	/** @copydoc HardwareBufferManagerInterface::_forceReleaseBufferCopies */
    virtual void _forceReleaseBufferCopies(U2HardwareVertexBuffer* sourceBuffer)
	{
		mImpl->_forceReleaseBufferCopies(sourceBuffer);
	}
	/** @copydoc HardwareBufferManagerInterface::_notifyVertexBufferDestroyed */
	void _notifyVertexBufferDestroyed(U2HardwareVertexBuffer* buf)
	{
		mImpl->_notifyVertexBufferDestroyed(buf);
	}
	/** @copydoc HardwareBufferManagerInterface::_notifyIndexBufferDestroyed */
	void _notifyIndexBufferDestroyed(U2HardwareIndexBuffer* buf)
	{
		mImpl->_notifyIndexBufferDestroyed(buf);
	}

};


U2EG_NAMESPACE_END

#endif

